Skip to content

Feat/lock simulation - #1

Merged
webvoltz merged 27 commits into
mainfrom
feat/lock-simulation
Sep 21, 2026
Merged

webvoltz merged 27 commits into
mainfrom
feat/lock-simulation

Conversation

@webvoltz

Copy link
Copy Markdown
Owner

No description provided.

rahulwebvoltz and others added 27 commits September 7, 2026 16:55
Add package.json description/keywords/repository for the public
Webvoltz profile listing, commit the ESLint/Prettier/Husky/commitlint/
Gitleaks tooling baseline, and remove unused Vite/React starter assets
(react.svg, default vite.svg favicon replaced with a project favicon).
Bump react, zustand, tailwindcss, vite, eslint, and typescript-eslint
to their latest versions within existing semver ranges. Clears all 40
pnpm audit advisories (28 high, 10 moderate, 2 low) that were pinned
in the lockfile against older transitive resolutions.
Replace the two alert() call sites (8-seat cap, fake payment redirect)
with controlled UI state: seatStore now exposes an aria-live feedback
message rendered by a new Toast component, and BookingSummary shows an
inline confirmation panel instead of blocking on a browser dialog.
Configure Vitest with jsdom + Testing Library and cover the core
booking flows: seat selection/deselection, the 8-seat cap surfacing
feedback instead of alert(), clearing the selection, the persisted-
seats localStorage round trip, and arrow-key/Enter/Space keyboard
navigation across rows including skipping unavailable seats.
Add GitHub Actions pipeline running on every push/PR: install (frozen
lockfile), quality (format-check + lint + typecheck), test, build,
plus a full-history Gitleaks secret scan and commitlint on the commit
range being merged.
Add the problem statement, 15,000+ seat rendering notes (section-
level SVG collapsing), the case for SVG over canvas/WebGL, the
Zustand persistence strategy, accessibility notes, and a Mermaid
architecture diagram to the README, plus docs/engineering-notes.md
covering the tradeoffs and remaining performance bottlenecks in
detail.
actions/checkout@v4, actions/setup-node@v4, and pnpm/action-setup@v4
still run on the Node.js 20 runtime, which GitHub Actions now flags
as deprecated. Bump each to its v5 major, where all three switched to
Node.js 24.
- Add Booking History: past purchases list, click a booking to highlight its seats on the map
- Make the sidebar responsive: full-screen drawer below 1024px, persistent panel above it
- Pin Total/Proceed to Pay outside the scroll area so it stays reachable with many seats picked
- Add a confirm dialog before clearing selected seats, replacing the instant clear action
- Trap focus behind open drawers/dialogs so keyboard users can't reach hidden controls
- Add page title/description, robots.txt, and llms.txt for search engines and AI crawlers
- Remove unused Stage/SectionSkeleton components and refresh generated sample data
- Update and extend tests to cover booking history and the new store actions
- Upgraded ESLint to strictTypeChecked + stylisticTypeChecked with the full required rule set — this surfaced and fixed real bugs: a missing-return path in Section.tsx, unsafe DOM element casts in Seat.tsx (now properly typed with SVGGElement generics), and blind JSON.parse/fetch().json() casts in seatStore.ts/useVenue.ts replaced with real runtime type guards instead of trusting the data
- Added missing tsconfig strictness options (noImplicitReturns, noImplicitOverride, forceConsistentCasingInFileNames, noPropertyAccessFromIndexSignature, allowUnreachableCode, allowUnusedLabels)
- Pre-commit hook now runs the full quality check and production build, not just lint-staged
- Commitlint header/body/footer limits relaxed to 1000 chars, matching the org standard
- Every dependency pinned to an exact version; added a security:audit script
- Added .nvmrc/.npmrc, a packageManager field, and aligned CI's pnpm version (10 → 11) to match what's actually installed — this was a real, previously-undocumented mismatch
- Enabled test coverage reporting (V8) with thresholds set as a floor against regression — still well below the org standard's targets, tracked honestly in engineering-notes.md
- Fixed stale doc references pointing at BookingSummary.tsx that should've said SeatSelectionFooter.tsx
The seating map used to zoom by resizing the SVG's own width/height and
pan by relying on the browser's native scrollbars, which made it
impossible to keep any point stationary while zooming and gave no way
to intercept trackpad pinch gestures (they just triggered the
browser's own page zoom).

Replace it with a single CSS transform (translate + scale) driven by
`zoom`/`pan` state in the store:

- utils/viewTransform.ts: pure, unit-tested math for fit-to-viewport
  zoom, pan clamping, and re-anchoring pan/zoom around a pointer so
  the content under the cursor stays put while zooming.
- VenueMap now renders the map inside a transformed wrapper instead of
  resizing the SVG element itself.
- App.tsx anchors zoom-in/out buttons on the viewport center, and adds
  a native wheel listener so ctrl/cmd+wheel (how browsers report
  trackpad pinch) zooms toward the pointer while a plain wheel/scroll
  pans. Panning moved from mouse events + scrollLeft/Top to Pointer
  Events with capture, so a single-finger touch drag also pans. Pointer
  capture is only claimed once real drag movement crosses a small
  threshold - claiming it on every pointerdown would redirect that
  pointer's click to the map container instead of whatever was
  actually pressed (a seat, a section, a zoom button), which would
  silently break every click-driven interaction on the map.
- Section.tsx's zoom-to-section-on-click effect now sets the new
  pan/zoom state directly instead of calling scrollTo() on the old
  scroll container.
Every view change (button zoom, reset, and zooming into a section on
click) animated over the same flat 200ms - fine for a small button
nudge, but a jarring snap for the much bigger jump from the overview
to 8x zoom on one section.

Add a transition duration alongside zoom/pan in the store (setView
now takes it as a third argument) so each caller can pick its own
pace: 200ms for discrete small jumps (buttons, reset), 650ms for
section focus. VenueMap also switches from a linear ease-out to a
cubic-bezier ease-out curve for a visibly smoother deceleration.
Clicking a section always zoomed to a flat 8x, which overshot for
larger sections - part of the section ended up cropped off-screen
instead of the whole thing being visible.

Compute the zoom from the section's own seat bounding box (padded a
bit so seats and the label aren't flush against the screen edge) via
the same fit-to-viewport math already used for the venue-level reset
view, so bigger sections zoom in less and smaller ones zoom in more.

While reworking that effect, also fix the pan calculation to account
for the section's own "explode" offset (the small outward nudge
applied while it's active) - previously omitted, which left the
focused section off-center by that offset.
pnpm start now runs vite preview, serving the dist/ output from
pnpm build - previously the only way to run a production build
locally was the less-discoverable pnpm preview.
Rounding seat x/y to whole pixels turned the circular row arcs into a
visible zig-zag, since adjacent seats are only ~3-4 units apart -
about one seat-box width. Rounding to 2 decimals keeps the arcs smooth
while still keeping the generated file readable.
Adds a deterministic best-seats solver (view- or price-priority,
strictly-ordered tiebreaks) and a sidebar finder that runs it and
selects/focuses the winning block, so a group no longer has to hand-pick
seats one at a time.
Seats can now be genuinely held rather than just statically flagged:
selecting a seat starts a 5-minute checkout deadline, venue.json's
sample `held` seats get real staggered expiries instead of never
releasing, and a BroadcastChannel-based protocol shares holds and
sales across tabs of the same browser (with a deterministic tie-break
for simultaneous picks, and a no-op fallback when the channel is
unavailable). Status derivation moves out of Seat.tsx into a single
pure resolver, which also fixes two bugs along the way: seats
re-rendered on every zoom tick instead of only when the label
threshold was crossed, and the price in every seat's aria-label read
the raw tier number instead of its dollar amount.
Raises the coverage regression floor in vite.config.ts to match the
actual numbers after the two features' near-100%-covered utility
modules landed, and updates the README, engineering notes, and the
UI/UX flow reference so they describe current behavior instead of the
now-stale "held/reserved never expire, no live-update mechanism"
gap the flow doc used to call out.
Section.tsx always fit the whole section's bounding box on activation,
so viewing a past booking or a best-seats result opened the right
section but framed all ~1,500 seats instead of the handful that
actually mattered. Generalizes the fix into one store field,
mapFocusSeatIds, that selectSeatBlock and viewBooking both set and
setActiveSection clears, so a manual section click still fits the
whole section as before.
…loating selection toolbar

Moves the "Seat Status" legend out of the sidebar into an always-visible
card in the map's top-right corner, freeing sidebar space and matching
how real ticketing UIs keep the map's own chrome self-contained.

Adds a floating black toolbar that appears above the map whenever any
seat is selected: live seat count, running total, a mirror of the
checkout countdown (most useful on mobile, where the sidebar - and its
own timer - starts hidden), and a one-click Remove to clear the cart.

Raises the coverage floor to match the new fully-tested components.
Every existing holdChannel/useHoldSync test drives the protocol
through a fake in-memory bus, since jsdom has no BroadcastChannel at
all. That proves the protocol logic is correct but never proves the
real transport actually delivers a message from one tab to another.
Runs in Node's own test environment instead (which does implement
BroadcastChannel) so two independent createHoldChannel instances can
stand in for two real browser tabs with no fake in the loop.
…tial notes only

Cuts multi-line "why" prose down to at most one short line, and only
where it explains a genuinely non-obvious constraint or invariant
(e.g. the pruneHolds reference-identity guarantee the perf story
depends on, the resolver's held-before-selected fail-safe ordering).
Comment-only change - no logic, JSX, or type changes.
@webvoltz
webvoltz merged commit b626fd4 into main Sep 21, 2026
8 checks passed
webvoltz added a commit that referenced this pull request Sep 21, 2026
webvoltz pushed a commit that referenced this pull request Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants